home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-02-01 | 21.0 KB | 532 lines | [TEXT/CCL2] |
- ;-*- Mode: Lisp; Package: CCL -*-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; edit-callers.lisp
- ;;copyright © 1992, 1993, Apple Computer, Inc.
- ;;
- ;;
- ;; pops up a dialog allowing you to choose from the callers of FUNCTION.
- ;;
- ;;
- ;
- ; usage: (edit-callers function)
- ;
- ; Uses %MAP-LFUNS which may cease to exist in a future version
- ; of MCL.
- ; This version uses smarter map-imms, requiring lots of specials, time
- ; cut from .972 to .718 or so
-
- ;;;;;;;;;;;;;;;;;;;;;;;;; edit history
- ; shortcomings
- ; doesn't find things in plists, hash tables, action-function slots etc...
- ; but if there ain't no name it won't have source file info anyway (unless user does special stuff)
- ; 06/22/92 alice make initial hash table big enough for CCL at least
- ; 06/17/92 alice fix for no callers, somewhat faster, fix fix for duplicate methods
- ; control-c and on tools menu, callers (setf ...) should work
- ; 06/16/92 alice fix for encapsulated things, weed out duplicate methods
- ; 06/15/92 alice ; extended alan ruttenberg's version to deal with swapped functions,
- ; interpreted functions, and copying gc
-
- (in-package :ccl)
-
- (def-fred-command (:control #\c) 'ed-edit-callers)
-
- (defparameter %edit-callers-string "")
- (defvar *edit-callers-dialog* nil)
-
- (defmethod ed-edit-callers ((w fred-mixin) &optional pos)
- (let ((form (ignore-errors (ed-current-sexp w pos))))
- (cond ((not form)(edit-callers-dialog))
- ((or (symbolp form)(and (consp form)(eq (car form) 'setf)))
- (edit-callers form))
- (t (ed-beep)))))
-
- (defun edit-callers-dialog (&aux (initial-string %edit-callers-string))
- (let ((w (front-window)))
- (when (and w (typep w 'fred-window))
- (multiple-value-bind (b e)(selection-range w)
- (when (neq b e)
- (setq initial-string (buffer-substring (fred-buffer w) b e))))))
- (if (and *edit-callers-dialog*
- (wptr *edit-callers-dialog*))
- (let ((di (current-key-handler *edit-callers-dialog*)))
- (when di
- (set-dialog-item-text di initial-string)
- (set-selection-range di 0 (length initial-string)))
- (window-select *edit-callers-dialog*))
- (setq *edit-callers-dialog*
- (get-string-from-user
- "Enter the name of a symbol. The callers will be shown."
- :window-title "Edit Callers"
- :initial-string initial-string
- :modeless t
- :action-function
- #'(lambda (new-string)
- (let (sym)
- (unless (equal new-string "")
- (setq %edit-callers-string new-string)
- (setq sym (read-from-string new-string)) ;might be a list like (setf boo)
- (edit-callers sym))))))))
-
- ; in the right place?
- (eval-when (:execute :load-toplevel)
- (let* ((menu *tools-menu*)
- (item (make-instance 'menu-item
- :menu-item-title "Edit Callers…"
- :menu-item-action 'edit-callers-dialog))
- (item-list (slot-value menu 'item-list))
- last)
- (apply #'remove-menu-items menu item-list)
- (do ((l item-list (cdr l)))
- ((null l))
- ; we assume there is one and it isn't first
- (when (string= (slot-value (car l) 'title) "-")
- (rplacd last (cons item l))
- (return))
- (setq last l))
- (apply #'add-menu-items menu item-list)))
-
-
- (require :lapmacros)
- #|
- (defmacro loop-over-immediates ((var type function) &body body)
- `(let ((lfunv (%lfun-vector ,function)))
- (let ((nm (lfun-vector-name lfunv)))
- (dotimes (i (%count-immrefs lfunv))
- (declare (fixnum i))
- (let ((,var (%nth-immediate lfunv i)))
- (when (neq im nm)
- ,@body))))))
- |#
-
-
-
- (defvar *function-parent-table* nil)
-
-
- (defun clear-function-parent-table ()
- (setq *function-parent-table* nil))
-
- ; make it a very weak hash table
- (defvar *pre-gc-hook* #'clear-function-parent-table)
-
- (defun copying-gc-p () ; if nz copying gc is on
- (neq 0 (lap-inline ()
- (move.l (a5 $Palt_dynamic_cons_area) acc))))
-
- (defun full-gccount ()
- (flet ((ptrcount (A5offset ephem)
- (if ephem
- (%get-unsigned-word (%get-ptr (%get-ptr (%currentA5) A5offset) $cons-area.pgc-count))
- 0)))
- (ptrcount $Pdynamic_cons_area t)))
-
- (eval-when (:compile-toplevel :execute)
- ; argument must be a variable
- (defmacro lfun-vector-attributes (lfunv)
- `(lap-inline () (:variable ,lfunv)
- (move.l (varg ,lfunv) atemp0)
- (move.w (atemp0 $lfv_attrib) acc)
- (ext.l acc)
- (mkint acc)))
-
- (defmacro lfun-vector-bits (lfunv)
- `(lap-inline () (:variable ,lfunv)
- (move.l (varg ,lfunv) atemp0)
- (move.l (atemp0 $lfv_bits) acc)
- (mkint acc)))
-
- ; macro maybe not be worth the trouble
- (defmacro global-function-p (random &optional name)
- (let ((thing (gensym)))
- `(let* ((,thing ,random)
- (name ,(or name `(function-name ,thing))))
- (and name
- (or (not (or (symbolp name)(and (consp name)(eq (car name) 'setf)))) ; maybe its (setf baz)
- (let ((fn (fboundp name)))
- (and fn
- (progn
- (when (consp fn)(setq fn (car fn))) ; macro expanders!
- (eq ,thing fn)))))
- name))))
- )
-
- (defun lfun-closure-p (lfun)
- (lap-inline () (:variable lfun)
- (move.l (varg lfun) atemp0)
- (move.w (atemp0 2) dy)
- (move.l nilreg acc)
- (if# (eq (cmp.w ($ $sp-funcall_cclosure) dy))
- (add.w ($ $t_val) acc))))
-
- ; make a macro ?
- (defun puthash-parent (im fun)
- (when (functionp im) ; was (or (functionp im)(eq imtype $sym.fapply))
- (if (global-function-p fun)
- (setf (gethash im *function-parent-table*) fun)
- (let ((ht (gethash im *function-parent-table*)))
- (if (not ht)
- (setf (gethash im *function-parent-table*) fun)
- (unless (eq ht fun)
- (if (consp ht)
- (when (not (memq fun ht))(nconc ht (list fun)))
- (if (not (global-function-p ht))
- (setf (gethash im *function-parent-table*) (list ht fun))))))))))
-
- ; all nil excludes swapped functions, default is include (can be very slow) (.739)
- (defun callers (function &optional (all t)
- &aux cfun callers gccount retry loadp)
- (declare (special cfun function callers))
- (declare (optimize (speed 3)(safety 0)))
- (if *function-parent-table*
- (clrhash *function-parent-table*)
- (setq *function-parent-table* (make-hash-table :size 700 :test 'eq :weak :value)))
- (if (and (symbolp function) (fboundp function))
- (setq cfun (symbol-function function)))
- (if (and (consp function)(eq (car function) 'setf))
- (let ((nm (cadr function)))
- (setq function (or (%setf-method nm)
- (and (setq nm (setf-function-name nm))
- (fboundp nm)
- nm)
- function))))
- (when (copying-gc-p) (setq gccount (full-gccount)))
- (flet ((do-it (fun)
- (declare (special fun))
- (when (and gccount (neq gccount (full-gccount)))
- (throw 'losing :lost))
- (let ((lfunv (%lfun-vector fun loadp)))
- (when lfunv
- (let ((bits (lfun-vector-bits lfunv)))
- (declare (fixnum bits))
- (unless (or (and (logbitp $lfbits-cm-bit bits)(not (logbitp $lfbits-method-bit bits))) ; combined method
- (and (logbitp $lfbits-trampoline-bit bits)(lfun-closure-p fun))) ; closure (interp or compiled)
- (if (logbitp $lfbits-evaluated-bit bits)
- (when (callers-interp fun function cfun)
- (push fun callers))
- (when (or loadp (not (logbitp $lfatr-slfunv-bit (the fixnum (lfun-vector-attributes lfunv)))))
- (let ((nm (lfun-vector-name lfunv)))
- (declare (special nm))
- (%map-lfimms
- lfunv
- #'(lambda (im)
- (when (and (or (eq function im)
- (and cfun (eq cfun im)))
- (neq im nm))
- (push fun callers))
- (when (functionp im) ; was (or (functionp im)(eq imtype $sym.fapply))
- (if (global-function-p fun nm)
- (setf (gethash im *function-parent-table*) fun)
- (let ((ht (gethash im *function-parent-table*)))
- (if (not ht)
- (setf (gethash im *function-parent-table*) fun)
- (unless (eq ht fun)
- (if (consp ht)
- (when (not (memq fun ht))(nconc ht (list fun)))
- (if (not (global-function-p ht))
- (setf (gethash im *function-parent-table*) (list ht fun))))))))))))))))))))
- (declare (dynamic-extent #'do-it))
- (loop
- (cond ((eq :lost (catch 'losing
- (%map-lfuns #'do-it)))
- (when retry (error "Callers is losing"))
- (setq callers nil)
- (setq retry t))
- (t (return))))
- (when all (setq loadp t)(map-swapped-lfuns #'do-it))
- ;(%map-static-lfuns #'do-it t) ; there are only 4 of these - is it worth the trouble?
- ; Get rid of garbage methods - do we like this?
- ; what about generic flet?
- (delete-if #'(lambda (thing)
- (or (functionp thing)
- (and (typep thing 'method)
- (let ((gf (fboundp (method-name thing))))
- (not (and (typep gf 'standard-generic-function)
- (memq thing (%gf-methods gf))))))))
- (delete-duplicates (mapcar 'top-level-caller callers)))))
-
-
-
- (defun top-level-caller (function &optional the-list)
- (or (global-function-p function)
- (let ((name (function-name function)))
- (and name (function-encapsulation name) name))
- (let ((caller function) next)
- (loop
- (setq next (gethash caller *function-parent-table*))
- (if next
- (cond ((consp next)
- (when (null the-list)(push function the-list))
- (return
- (dolist (c next)
- (when (not (memq c the-list))
- (let ((res (top-level-caller c the-list)))
- (when (and res (not (functionp res)))
- (return res)))))))
- (t (let ((res (global-function-p next)))
- (when res (return res)))
- (when (null the-list)(push function the-list))
- (when (memq next the-list) (return))
- (push next the-list)
- (setq caller next)))
- (return caller))))
- function))
-
-
- (defun edit-callers (function &key
- (include-swapped t)
- (modelessp t)
- (window-title (format nil "Callers of ~A" function))
- (default-button-text "Edit")
- &aux w)
- (declare (dynamic-extent initargs))
- (let ((callers (with-cursor *watch-cursor* (callers function include-swapped))))
- (setq callers (sort callers #'edit-definition-spec-lessp)) ; hmm sorts by specializers
- (if (not callers)
- (progn (format t "There are no callers of ~S" function)(ed-beep))
- (setq w
- (select-item-from-list
- callers
- :window-title window-title
- :table-print-function
- #'(lambda (thing &optional (stream t))
- (if (typep thing 'standard-method)
- (let ((qualifiers (%method-qualifiers thing)))
- (format stream "<~s ~s ~s>"
- (%method-name thing)
- (case (length qualifiers)
- (0 :primary)
- (1 (car qualifiers))
- (t qualifiers))
- (mapcar #'(lambda (class)
- (if (consp class)
- class
- (or (class-name class) class)))
- (%method-specializers thing))))
- (format stream "~s" thing)))
- :modeless modelessp
- :default-button-text default-button-text
- :action-function
- #'(lambda (list)
- (if (option-key-p) (window-close w))
- (edit-definition (car list))))))))
-
- (defun map-swapped-lfuns (function)
- (setq function (coerce-to-function function))
- (let ((p (%get-long (%currenta5) $slfuns_start))
- (q (%get-long (%currenta5) $slfuns_end)))
- (loop
- (when (eq p q)(return))
- (funcall function (lap-inline (p) (jsr_subprim $sp-getulong)))
- (setq p (%i+ p 8)))))
-
- #|
- ;Map function over all static lfuns
- (defun %map-static-lfuns (function)
- (setq function (coerce-to-function function))
- (lap-inline ()
- (:variable function)
- (with-preserved-registers #(dsave0 asave0)
- (move.l (varg function 8) asave0)
- (move.l (a5 $Pstatic_cons_area) atemp1)
- (move.l (atemp1 $cons-area.gspace-start) atemp0)
- (move.l (atemp1 $cons-area.gspace-end) dsave0)
- (prog#
- (move.l @atemp0 da)
- (if# (ne (cmp.b ($ $object-header) da))
- (add ($ 8) atemp0)
- elseif# (eq (cmp.w ($ $symbol-header) da))
- (lea (atemp0 $sym_size) atemp0)
- else#
- (if# (eq (cmp.w ($ $lfunv-header) da))
- (add.w ($ $t_vector) atemp0)
- (vpush atemp0)
- (move.l ($ (+ $v_data $t_lfun)) arg_z)
- (add.l atemp0 arg_z)
- (set_nargs 1)
- (jsr @asave0)
- (move.l (a5 $Pstatic_cons_area) atemp1) ; why
- (vpop atemp0)
- (sub.w ($ $t_vector) atemp0))
- (move.l ($ 15) da)
- (add.l (atemp0 (+ $t_vector $v_log)) da)
- (and.l ($ #x00FFFFF8) da)
- (add.l da atemp0))
- (until# (geu dsave0 atemp0)))))
- nil)
- |#
-
- (defun callers-interp (function target ctarget)
- (let* ((lfunv (%lfun-vector function))
- (body (%%nth-immediate lfunv 1)) ; might be 0 if $lfatr-noname-bit, or 2 if keys, or 3 if d.a.c.
- calls-target)
- ; crock!!!!! - what is the right way to do this?
- (when (not (consp body))
- (dotimes (i (%count-immrefs lfunv))
- (let ((it (%%nth-immediate lfunv i)))
- (when (consp it)(setq body it)(return)))))
- (labels ((calls-in-progn (body)
- (dolist (expr body)
- (calls-in-expr expr)))
- (calls-in-expr (expr)
- (when (consp expr)
- (let ((car (car expr)))
- (if (consp car)
- (ecase (car car)
- ; 34 special forms
- ((block progn if tagbody progv locally unwind-protect
- multiple-value-list multiple-value-prog1
- without-interrupts)
- (calls-in-progn (cdr expr)))
- (catch (calls-in-progn (cddr expr)))
- (multiple-value-call (calls-in-progn (cdr expr)))
- ((the return-from throw) (calls-in-expr (third expr)))
- ((%with-specials eval-when) (calls-in-progn (cddr expr)))
- ((%local-ref %special-ref quote %special-declare go
- %closure-ref %special-bind))
- (%local-fref
- (puthash-parent (second car) function)
- (calls-in-progn (cdr expr)))
- (%init&bind (calls-in-expr (third expr)))
- ((let let* compiler-let)
- (let ((args (second expr)))
- (dolist (a args)
- (when (consp a) (calls-in-expr (second a))))
- (calls-in-progn (cddr expr))))
- ((flet labels macrolet symbol-macrolet)
- (calls-in-progn (cddr expr)))
- (setq
- (do ((l (cdr expr)(cddr l)))
- ((null l))
- (calls-in-expr (second l))))
- (function
- (let ((fn (second expr)))
- (if (symbolp fn)
- (when (eq fn target)(setq calls-target t))
- (progn
- (when (eq target ctarget)(setq calls-target t))
- (puthash-parent (second expr) function))))))
- (case car
- (%local-fref
- (puthash-parent (second expr) function))
- (quote)
- (t
- (when (eq car target)
- (setq calls-target t))
- (calls-in-progn (cdr expr)))))))))
- (calls-in-progn body)
- calls-target)))
- #|
- ; copy of %nth-immediate without calls to %count-immrefs and #'< - saves a lot!
- (defun %%nth-immediate (lfv i)
- (new-lap
- (:variable lfv i)
- (move.l (varg lfv) atemp0)
- (getvect atemp0 da)
- (if# (ne (btst.w ($ $lfatr-slfunv-bit) (atemp0 (- $lfv_attrib $v_data))))
- (sub.l ($ 4) da))
- (lea (atemp0 da.l 0) atemp1)
- (lea (atemp0 $t_lfun) atemp0)
- (move.l (varg i) arg_y)
- @loop
- (move.l ($ 0) da)
- (move.b -@atemp1 da)
- (if# (cs (add.b da da))
- (rol.w ($ 8) da)
- (move.b -@atemp1 da)
- (rol.w ($ 8) da))
- (add.l da atemp0)
- (bif (pl (sub.l '1 arg_y)) @loop)
- (move.l @atemp0 acc)
- (if# (eq (ttagp ($ $t_symbol) acc da)) ; sets da.l to 0
- (move.l acc atemp0)
- (if# (ne (tst.w (atemp0 (- $t_symbol))))
- (moveq $sym.gvalue da)
- (if# (ne (tst.w (atemp0 (- (+ $t_symbol $sym.gvalue)))))
- (moveq $sym.fapply da)))
- (sub.l da acc)
- (ext.w da)
- (ext.l da)
- (mkint da)
- else#
- (move.l nilreg da))
- (vpush acc)
- (vpush da)
- (set_nargs 2)
- (jmp_subprim $sp-nvalret)))
- |#
- ; this one returns a single value - saving 8.5%
- (defun %%nth-immediate (lfv i)
- (lap-inline ()
- (:variable lfv i)
- (move.l (varg lfv) atemp0)
- (getvect atemp0 da)
- (if# (ne (btst.w ($ $lfatr-slfunv-bit) (atemp0 (- $lfv_attrib $v_data))))
- (sub.l ($ 4) da))
- (lea (atemp0 da.l 0) atemp1)
- (lea (atemp0 $t_lfun) atemp0)
- (move.l (varg i) arg_y)
- @loop
- (move.l ($ 0) da)
- (move.b -@atemp1 da)
- (if# (cs (add.b da da))
- (rol.w ($ 8) da)
- (move.b -@atemp1 da)
- (rol.w ($ 8) da))
- (add.l da atemp0)
- (bif (pl (sub.l '1 arg_y)) @loop)
- (move.l @atemp0 acc)
- (if# (eq (ttagp ($ $t_symbol) acc da)) ; sets da.l to 0
- (move.l acc atemp0)
- (if# (ne (tst.w (atemp0 (- $t_symbol))))
- (moveq $sym.gvalue da)
- (if# (ne (tst.w (atemp0 (- (+ $t_symbol $sym.gvalue)))))
- (moveq $sym.fapply da)))
- (sub.l da acc))))
-
- ; Calls function f with args (imm) on each immediate in lfv.
- (defun %map-lfimms (lfv f)
- (lap-inline (lfv f)
- (with-preserved-registers #(asave0 asave1 dsave0 dsave1 dsave2)
- (move.l arg_y asave0) ; vect
- (move.l arg_z asave1) ; fun
- (vsize asave0 dsave0)
- (lea (asave0 $lfv_attrib) atemp0)
- (move.w atemp0@+ dx)
- (if# (ne (btst ($ $lfatr-immmap-bit) dx))
- (if# (ne (btst ($ $lfatr-slfunv-bit) dx))
- (sub.l ($ 4) dsave0))
- (moveq 0 dsave1)
- (moveq '0 dsave2)
- (until# (eq (progn (moveq 0 acc)
- (lea (asave0 dsave0.l $v_data) atemp1)
- (sub.l ($ 1) dsave0)
- (move.b -@atemp1 acc)))
- (if# (cs (add.b acc acc))
- (ror.w ($ 8) acc)
- (move.b -@atemp1 acc)
- (sub.l ($ 1) dsave0)
- (ror.w ($ 8) acc))
- (add.l acc dsave1)
- (moveq 0 arg_z)
- (move.l (asave0 dsave1.l $lfv_lfun) arg_y)
- (if# (ne (dtagp arg_y $t_symbol))
- (move.l arg_y atemp0)
- (if# (ne (tst.w -@atemp0))
- (add.w ($ 8) arg_z)
- (sub.w ($ 8) atemp0)
- (if# (ne (tst.w @atemp0))
- (add.w ($ 8) arg_z))))
- (sub.l arg_z arg_y)
- ;(mkint arg_z)
- (mkint dsave0)
- (mkint dsave1)
- (move.l arg_y arg_z)
- (add.l '1 dsave2)
- (set_nargs 1)
- (jsr @asave1)
- (getint dsave1)
- (getint dsave0)))))
- nil)
-
-
-